#include "mini_redis/Rdb.h"
#include "mini_redis/KeyValueStore.h"
#include <cstddef>
#include <fcntl.h>
#include <filesystem>
#include <string>
#include <sys/stat.h>
#include <sys/types.h>
#include <system_error>
#include <unistd.h>

namespace mini_redis {

#if 0
    joinPath 的封装本质上是将 “路径拼接” 这个独立的功能抽离出来，
既保证了路径拼接的正确性（处理各种边缘情况），又实现了代码复用和可维护性。

    joinPath 被声明为 static，
核心原因是限制该函数的作用域的仅当前编译单元（即当前 .cpp 文件），
避免其成为命名空间的 “公共接口”，从而实现封装内部细节、避免命名冲突的目标。

#endif
static std::string joinPath(const std::string& dir,
                            const std::string& file)
{
    if (dir.empty())
        return file;

    if (dir.back() == '/')
        return dir + file;

    return dir + "/" + file;
}

std::string Rdb::path() const
{
    return joinPath(opts_.dir, opts_.filename);
}

#if 0
    把内存中的数据（字符串、哈希、有序集合）按照固定格式写入到磁盘文件（RDB 文件）
中，实现数据的持久化保存。


MRDB2格式：
Header: MRDB2\n
Strings: STR count\n then per line: klen key vlen value expire_ms\n
Hash: HASH count\n then per hash: klen key expire_ms num_fields\n then num_fields lines: flen field vlen value\n 
ZSet: ZSET count\n then per zset: klen key expire_ms num_items\n then num_items lines: score member_len member\n
#endif
bool Rdb::save(const KeyValueStore& store, std::string& err) const
{
    if (!opts_.enabled)
        return true;

    std::error_code ec;
    std::filesystem::create_directories(opts_.dir, ec);
    int fd = ::open(path().c_str(), O_CREAT | O_TRUNC | O_WRONLY, 0644);
    if (fd < 0)
    {
        err = "open rdb failed";
        return false;
    }

    Defer d([fd]() { ::close(fd); });

    auto snap_str  = store.snapshot();
    auto snap_hash = store.snapshotHash();
    auto snap_zset = store.snapshotZSet();

    // 写文件头
    std::string header("MRDB2\n");
    if (::write(fd, header.data(), header.size()) < 0)
    {
        err = "write header";
        return false;
    }

    // 写字符串
    std::string line =
        std::string("STR ") + std::to_string(snap_str.size()) + "\n";
    if (::write(fd, line.data(), line.size()) < 0)
    {
        err = "write str cnt";
        return false;
    }
    for (auto& kv : snap_str)
    {
        const std::string& k = kv.first;
        const ValueRecord& v = kv.second;

        std::string rec;
        rec.append(std::to_string(k.size()))
            .append(" ")
            .append(k)
            .append(" ")
            .append(std::to_string(v.value.size()))
            .append(" ")
            .append(v.value)
            .append(" ")
            .append(std::to_string(v.expire_at_ms))
            .append("\n");

        if (::write(fd, rec.data(), rec.size()) < 0)
        {
            err = "write str rec";
            return false;
        }
    }

    // 写哈希
    line = std::string("HASH ") + std::to_string(snap_hash.size()) + "\n";
    if (::write(fd, line.data(), line.size()) < 0)
    {
        ::close(fd);
        err = "write hash cnt";
        return false;
    }
    for (const auto& kv : snap_hash)
    {
        const std::string& k = kv.first;
        const HashRecord&  r = kv.second;
        std::string        rec_head;
        rec_head.append(std::to_string(k.size()))
            .append(" ")
            .append(k)
            .append(" ")
            .append(std::to_string(r.expire_at_ms))
            .append(" ")
            .append(std::to_string(r.fields.size()))
            .append("\n");
        if (::write(fd, rec_head.data(), rec_head.size()) < 0)
        {
            err = "write hash head";
            return false;
        }
        for (const auto& fv : r.fields)
        {
            std::string fline;
            fline.append(std::to_string(fv.first.size()))
                .append(" ")
                .append(fv.first)
                .append(" ")
                .append(std::to_string(fv.second.size()))
                .append(" ")
                .append(fv.second)
                .append("\n");
            if (::write(fd, fline.data(), fline.size()) < 0)
            {
                err = "write hash field";
                return false;
            }
        }
    }

    // 写有序集合
    line = std::string("ZSET ") + std::to_string(snap_zset.size()) + "\n";
    if (::write(fd, line.data(), line.size()) < 0)
    {
        err = "write zset cnt";
        return false;
    }
    for (const auto& flat : snap_zset)
    {
        const std::string& k = flat.key;
        std::string        rec_head;
        rec_head.append(std::to_string(k.size()))
            .append(" ")
            .append(k)
            .append(" ")
            .append(std::to_string(flat.expire_at_ms))
            .append(" ")
            .append(std::to_string(flat.items.size()))
            .append("\n");
        if (::write(fd, rec_head.data(), rec_head.size()) < 0)
        {
            err = "write zset head";
            return false;
        }
        for (const auto& it : flat.items)
        {
            const double       score  = it.first;
            const std::string& member = it.second;
            std::string        iline;
            iline.append(std::to_string(score))
                .append(" ")
                .append(std::to_string(member.size()))
                .append(" ")
                .append(member)
                .append("\n");
            if (::write(fd, iline.data(), iline.size()) < 0)
            {
                err = "write zset item";
                return false;
            }
        }
    }

#if 0
    强制将该文件描述符对应的文件的所有已修改数据和元数据（如文件大小、修改时间等）
从操作系统的内核缓冲区（内存中的临时存储区域）刷新到物理磁盘中。
#endif
    ::fsync(fd);
    return true;
}

bool Rdb::load(KeyValueStore& store, std::string& err) const
{
    // 1. 基础判断：功能未启用直接返回成功
    if (!opts_.enabled)
        return true;

    // 2. 打开文件（只读），Defer确保无论是否出错都关闭fd
    int fd = ::open(path().c_str(), O_RDONLY);
    if (fd < 0)
        return true;    // 无文件属正常情况
    Defer d([fd]() { ::close(fd); });

    // 3. 读取整个文件到内存（1MB缓冲区分批读）
    std::string data(1 << 20,
                     '\0');    // 1MB缓冲区（直接初始化，省resize）
    std::string file;
    while (true)
    {
        ssize_t r = ::read(fd, data.data(), data.size());
        if (r < 0)
        {
            err = "read rdb";
            return false;
        }    // 读失败
        if (r == 0)
            break;    // 读完退出
        file.append(data.data(), static_cast<size_t>(r));
    }

    // 通用辅助函数
    size_t pos = 0;
    // 辅助1：按行读取
    auto readLine = [&](std::string& out) -> bool {
        size_t e = file.find('\n', pos);
        if (e == std::string::npos)
            return false;
        out.assign(file.data() + pos, e - pos);
        pos = e + 1;
        return true;
    };

    // 辅助2：提取空格分隔的字段
    // 参数：当前解析位置（引用，更新进度）、目标行、输出字段
    auto extractToken = [](size_t& curr_pos, const std::string& line,
                           std::string& tok) -> bool {
        // 跳过前置空格
        size_t s = curr_pos;
        while (s < line.size() && line[s] == ' ')
            ++s;

        // 找字段结束位置
        size_t end = line.find(' ', s);
        if (end == std::string::npos)
        {    // 最后一个字段
            tok      = line.substr(s);
            curr_pos = line.size();
        }
        else
        {    // 中间字段
            tok      = line.substr(s, end - s);
            curr_pos = end + 1;
        }
        return true;    // 原逻辑恒成功，保留返回值兼容
    };

    // 辅助3：解析「键长+键+值长+值」，返回解析结束后的位置
    auto parseKV =
        [&](const std::string& line, std::string& key,
            std::string& val) -> size_t {    // 返回值：解析结束后的位置
        size_t      p = 0;
        std::string len_str;

        // 解析键长+键
        extractToken(p, line, len_str);
        int klen = std::stoi(len_str);
        key      = line.substr(p, static_cast<size_t>(klen));
        p += static_cast<size_t>(klen) + 1;    // 跳过键+空格

        // 解析值长+值
        extractToken(p, line, len_str);
        int vlen = std::stoi(len_str);
        val      = line.substr(p, static_cast<size_t>(vlen));
        p += static_cast<size_t>(vlen) + 1;    // 跳过值+空格

        return p;    // 返回解析完键值后的位置
    };
    // -----------------------------------------------------------------------------------

    // 4. 解析文件头
    std::string line;
    if (!readLine(line))
    {
        err = "bad magic";
        return false;
    }

    // 5. 处理旧版本 MRDB1（仅字符串）
    if (line == "MRDB1")
    {
        if (!readLine(line))
        {
            err = "no count";
            return false;
        }
        int count = std::stoi(line);

        for (int i = 0; i < count; ++i)
        {
            if (!readLine(line))
            {
                err = "trunc rec";
                return false;
            }

            std::string key, val, exp_str;

            // 拿到键值解析后的位置
            size_t line_pos = parseKV(line, key, val);

            // 从正确位置解析过期时间
            extractToken(line_pos, line, exp_str);
            int64_t exp = std::stoll(exp_str);

            store.setWithExpireAtMs(key, val, exp);
        }
        return true;
    }

    // 6. 验证新版本 MRDB2（不支持的版本直接报错）
    if (line != "MRDB2")
    {
        err = "bad magic";
        return false;
    }

    // 解析 MRDB2 各数据结构
    // 6.1 解析 STR 部分（字符串）
    if (!readLine(line) || line.rfind("STR ", 0) != 0)
    {
        err = (!line.empty() ? "no str tag" : "no str section");
        return false;
    }
    int str_count = std::stoi(line.substr(4));
    for (int i = 0; i < str_count; ++i)
    {
        if (!readLine(line))
        {
            err = "trunc str rec";
            return false;
        }

        std::string key, val, exp_str;
        size_t line_pos = parseKV(line, key, val);    // 拿到正确的起始位置
        extractToken(line_pos, line, exp_str);
        store.setWithExpireAtMs(key, val, std::stoll(exp_str));
    }

    // 6.2 解析 HASH 部分（哈希表）
    if (!readLine(line) || line.rfind("HASH ", 0) != 0)
    {
        err = (!line.empty() ? "no hash tag" : "no hash section");
        return false;
    }
    int hash_count = std::stoi(line.substr(5));
    for (int i = 0; i < hash_count; ++i)
    {
        if (!readLine(line))
        {
            err = "trunc hash head";
            return false;
        }

        size_t      p = 0;
        std::string len_str, key, exp_str, nfields_str;
        // 解析哈希表的「键长+键」
        extractToken(p, line, len_str);
        int klen = std::stoi(len_str);
        key      = line.substr(p, static_cast<size_t>(klen));
        p += static_cast<size_t>(klen) + 1;
        // 解析过期时间+字段数
        extractToken(p, line, exp_str);
        extractToken(p, line, nfields_str);
        size_t nf = static_cast<size_t>(std::stoi(nfields_str));

        // 解析哈希表的字段-值对
        std::vector<std::pair<std::string, std::string>> fvs;
        fvs.reserve(nf);
        for (size_t j = 0; j < nf; ++j)
        {
            if (!readLine(line))
            {
                err = "trunc hash field";
                return false;
            }

            std::string field, val;
            parseKV(line, field, val);    // 复用通用解析（字段=键，值=值）
            fvs.emplace_back(std::move(field), std::move(val));
        }

        // 存入哈希表
        for (const auto& fv : fvs)
            store.hset(key, fv.first, fv.second);
        if (std::stoll(exp_str) >= 0)
            store.setHashExpireAtMs(key, std::stoll(exp_str));
    }

    // 6.3 解析 ZSET 部分（有序集合）
    if (!readLine(line) || line.rfind("ZSET ", 0) != 0)
    {
        err = (!line.empty() ? "no zset tag" : "no zset section");
        return false;
    }
    int zset_count = std::stoi(line.substr(5));
    for (int i = 0; i < zset_count; ++i)
    {
        if (!readLine(line))
        {
            err = "trunc zset head";
            return false;
        }

        size_t      p = 0;
        std::string len_str, key, exp_str, nitems_str;
        // 解析有序集合的「键长+键」
        extractToken(p, line, len_str);
        key = line.substr(p, static_cast<size_t>(std::stoi(len_str)));
        p += static_cast<size_t>(std::stoi(len_str)) + 1;
        // 解析过期时间+元素数
        extractToken(p, line, exp_str);
        extractToken(p, line, nitems_str);
        int ni = std::stoi(nitems_str);

        // 解析有序集合的「分数+成员」
        for (int j = 0; j < ni; ++j)
        {
            if (!readLine(line))
            {
                err = "trunc zset item";
                return false;
            }

            size_t      q = 0;
            std::string score_str, mlen_str, member;
            extractToken(q, line, score_str);    // 解析分数
            extractToken(q, line, mlen_str);     // 解析成员长度
            member =
                line.substr(q, static_cast<size_t>(std::stoi(mlen_str)));

            store.zadd(key, std::stod(score_str), member);
        }

        // 设置有序集合过期时间
        if (std::stoll(exp_str) >= 0)
            store.setZSetExpireAtMs(key, std::stoll(exp_str));
    }
    return true;
}

}    // namespace mini_redis